/**
 * Created by cqb32_000 on 2016-12-18.
 */

var jsts = require("jsts");
var wktreader = new jsts.io.WKTReader();
var Cesium = require("cesium");
var ellipsoid = Cesium.Ellipsoid.WGS84;
var Utils = require(__dirname+"/Utils");
var moment = require("moment");
var config = require("../config");
var TILTSTEP = 0.2,AREAPERCENT = 0.02;
var _minstepseconds = 0.1;
var hasha = require('hasha');
var redis = require('redis');

var RDS_PORT = 6379,        //端口号
    RDS_HOST = '127.0.1.1',    //服务器IP
    RDS_PWD = '123456',
    RDS_OPTS = {},            //设置项
    client = redis.createClient(RDS_PORT,RDS_HOST,RDS_OPTS);

var Worker = function(time){
    this.time = time;
    this.satellite = time.sat;
    this.mission = time.mis;
    this.missionArea = this.mission.orignarea;
    this.misgeometry = wktreader.read(this.mission.remaindwkt);

    try {
        this.getMaxUnionTilt();
    }catch (e){
        console.trace(e);
    }
};

Worker.prototype = {

    getMaxUnionTilt: function(){
        var max = 0, maxtilt, maxgeometry, maxintersection;
        var time = this.time;

        this.getMaxFromDB((ret)=>{
            if(ret && ret.fromDB){
                max = ret.max;
                maxtilt = ret.maxtilt;
                maxgeometry = ret.maxgeometry;
                maxintersection = ret.maxintersection;

                calculateBestTilt.apply(this, []);

            }else{
                var scope = this;
                var stripes = [];
                var exec = function(tilt){
                    var positions = Utils.getStripeByTime(scope.satellite, tilt, time.start, time.end, Cesium.JulianDate.secondsDifference(time.end, time.start));
                    var wkt = scope.convertToWKT(positions);
                    stripes.push({
                        wkt: wkt,
                        tilt: tilt
                    });
                    if(Math.abs(tilt) <= scope.satellite.max_tilt) {
                        var geometry = wktreader.read(wkt);
                        if (scope.misgeometry.intersects(geometry)) {
                            var intersection = scope.misgeometry.intersection(geometry);
                            var area = intersection.getArea();
                            if (max < area) {
                                max = area;
                                maxtilt = tilt;
                                maxgeometry = geometry;
                                maxintersection = intersection;
                            }
                        }
                    }
                };

                //寻找最大覆盖面积的侧摆
                for(var tilt = 0; tilt <= this.satellite.sen_scenelength; tilt+= TILTSTEP) {
                    var ltilt = tilt;
                    var rtilt = -tilt;

                    exec(ltilt);
                    exec(rtilt);
                }

                scope.saveStripes(stripes);

                calculateBestTilt.apply(scope, []);
            }
        });

        var calculateBestTilt = function () {
            if (maxgeometry) {
                var lastgeom = this.misgeometry.difference(maxgeometry);
                //调整条带侧摆使条带覆盖最小区域
                this.coverSmallArea(this.satellite, lastgeom, maxgeometry, time, maxtilt);
                if (time.tilt != undefined) {
                    var positions = Utils.getStripeByTime(this.satellite, time.tilt, time.start, time.end, Cesium.JulianDate.secondsDifference(time.end, time.start));
                    var wkt = this.convertToWKT(positions);
                    var geometry = wktreader.read(wkt);
                    lastgeom = this.misgeometry.difference(geometry);
                    var intersection = this.misgeometry.intersection(geometry);
                    var area = intersection.getArea();
                    //精确到小数点后两位
                    time.tilt = Math.round(time.tilt * 100) / 100;
                    time.area = area;
                } else {
                    time.tilt = Math.round(maxtilt * 100) / 100;
                    time.area = max;
                }

                time.start = this.optimizeStripe(this.satellite, time.tilt, time, maxintersection, true);
                time.end = this.optimizeStripe(this.satellite, time.tilt, time, maxintersection, false);
                time.orbitnum = Utils.getOrbitnum(this.satellite, time.start);
                time.range = Cesium.JulianDate.secondsDifference(time.end, time.start);
                var scenelength = this.satellite.sen_scenelength;
                var scenenum = parseInt(time.range / scenelength);
                if (time.range % scenelength != 0 || !scenenum) {
                    scenenum += 1;
                }
                time.scenenum = scenenum;
                //剩余区域
                this.misgeometry = lastgeom;
            }

            var stripeWKT = "";
            if(time.tilt != undefined) {
                var pos = Utils.getStripeByTime(time.sat, time.tilt, time.start, time.end);
                stripeWKT = this.convertToWKT(pos);
            }

            process.send({
                time: time,
                start: moment(Cesium.JulianDate.toDate(time.start)).add(-this.satellite.sen_scenelength, "seconds").format("YYYY-MM-DD HH:mm:ss.SSS"),
                end: moment(Cesium.JulianDate.toDate(time.end)).add(this.satellite.sen_scenelength, "seconds").format("YYYY-MM-DD HH:mm:ss.SSS"),
                lastarea: this.misgeometry.getArea(),
                remaindwkt: this.misgeometry.toString(),
                stripeWKT: stripeWKT
            });
        }
    },

    getMaxFromDB: function(callback){

        var tle = hasha(this.satellite.sat_tle);
        var time = this.time;
        var time_key = moment(Cesium.JulianDate.toDate(time.start)).format("YYYYMMDDHHmmssSSS")+"_"
            +moment(Cesium.JulianDate.toDate(time.end)).format("YYYYMMDDHHmmssSSS");
        var key = this.mission.mis_id+"_"+this.mission.mis_order_modifytime+":"+this.satellite.sen_id+":"+tle+":"+time_key;

        client.get(key, (err, stripes)=>{
            stripes = JSON.parse(stripes);
            var max = 0,maxtilt, maxgeometry, maxintersection;

            if(stripes) {
                console.log("from redis", stripes.length);
                stripes.forEach((stripe)=> {
                    if(Math.abs(stripe.tilt) <= this.satellite.max_tilt) {
                        var geometry = wktreader.read(stripe.wkt);
                        if (this.misgeometry.intersects(geometry)) {
                            var intersection = this.misgeometry.intersection(geometry);
                            var area = intersection.getArea();
                            if (max < area) {
                                max = area;
                                maxtilt = stripe.tilt;
                                maxgeometry = geometry;
                                maxintersection = intersection;
                            }
                        }
                    }
                });
                callback ? callback({
                    fromDB: true,
                    max: max,
                    maxtilt: maxtilt,
                    maxgeometry: maxgeometry,
                    maxintersection: maxintersection
                }) : false;
            }else{
                callback ? callback() : false;
            }
        });
    },

    saveStripes: function(stripes){
        console.log("save...", stripes.length);
        var tle = hasha(this.satellite.sat_tle);
        var time = this.time;
        var time_key = moment(Cesium.JulianDate.toDate(time.start)).format("YYYYMMDDHHmmssSSS")+"_"
            +moment(Cesium.JulianDate.toDate(time.end)).format("YYYYMMDDHHmmssSSS");
        var key = this.mission.mis_id+"_"+this.mission.mis_order_modifytime+":"+this.satellite.sen_id+":"+tle+":"+time_key;

        client.set(key, JSON.stringify(stripes));
        client.expire(key, config.REDISEXP);
    },

    optimizeStripe: function(sat, tilt, time, misgeometry, isstart){
        var minstep = _minstepseconds / Cesium.TimeConstants.SECONDS_PER_MINUTE;
        var allminutes = Cesium.JulianDate.secondsDifference(time.end, time.start) / Cesium.TimeConstants.SECONDS_PER_MINUTE;
        var minutes, julianDate;

        var newtime;
        if(isstart){
            newtime = time.start;
        }else{
            newtime = time.end;
        }
        for (minutes = 0; minutes <= allminutes;) {
            var delta = isstart ? 1 : -1;
            julianDate = Cesium.JulianDate.addMinutes(newtime, delta * minutes, new Cesium.JulianDate());

            var pos = Utils.getPositionByTime(sat, julianDate);
            var nextpos = Utils.getPositionByTime(sat, Cesium.JulianDate.addMinutes(julianDate,delta * minstep, new Cesium.JulianDate()));
            var lrpos = Utils.getTripeLeftRightPos(sat, pos, tilt);
            var lp1 = ellipsoid.cartesianToCartographic(lrpos[0]);
            var cp1 = ellipsoid.cartesianToCartographic(pos);
            var rp1 = ellipsoid.cartesianToCartographic(lrpos[1]);

            lrpos = Utils.getTripeLeftRightPos(sat, nextpos, tilt);
            var lp2 = ellipsoid.cartesianToCartographic(lrpos[0]);
            var cp2 = ellipsoid.cartesianToCartographic(nextpos);
            var rp2 = ellipsoid.cartesianToCartographic(lrpos[1]);

            var poly = [lp1, lp2, cp2, rp2, rp1, cp1];

            if (this.intersects(poly, misgeometry)) {
                newtime = Cesium.JulianDate.clone(julianDate, new Cesium.JulianDate());
                break;
            }

            minutes = minutes + minstep;
        }

        return newtime;
    },

    /**
     * 计算是存在在误差范围内的最小面积区域,并调整侧摆使条带能够覆盖该区域
     * @param sat
     * @param misgeometry
     * @param stripegeometry
     * @param time
     * @param tilt
     */
    coverSmallArea: function(sat, misgeometry, stripegeometry, time, tilt){
        if(misgeometry.geometries) {
            var geometry;
            var min,mingeom;
            for(var i in misgeometry.geometries){
                var geom = misgeometry.geometries[i];
                if(geom.intersects(stripegeometry)) {
                    var area = geom.getArea();
                    if (!min) {
                        min = area;
                        mingeom = geom;
                    } else {
                        if (min > area) {
                            min = area;
                            mingeom = geom;
                        }
                    }
                }
            }
            if(min > stripegeometry.getArea()){
                time.tilt = tilt;
                return;
            }
            var percent = min / this.missionArea;
            if(percent <= AREAPERCENT){
                geometry = mingeom;
            }

            if(geometry){
                var finaltilt = this._coverSmallArea(sat, geometry, time, tilt, TILTSTEP);
                if(finaltilt != null){
                    time.tilt = finaltilt;
                }
            }
        }
    },

    /**
     * 调整侧摆使条带能够包含最小面积区域
     * @param sat
     * @param geometry
     * @param time
     * @param tilt
     * @param offset
     * @returns {*}
     * @private
     */
    _coverSmallArea: function(sat, geometry, time, tilt, offset){
        if(geometry){
            var lefttilt = tilt-offset;
            var righttilt = tilt+offset;
            if(lefttilt < -sat.max_tilt && righttilt > sat.max_tilt){
                return null;
            }
            if(lefttilt >= -sat.max_tilt){
                var positions = Utils.getStripeByTime(sat, lefttilt, time.start, time.end, Cesium.JulianDate.secondsDifference(time.end, time.start));
                var wkt = this.convertToWKT(positions);
                var geom = wktreader.read(wkt);
                if(geom.contains(geometry)){
                    return lefttilt;
                }
            }
            if(righttilt <= sat.max_tilt){
                var positions = Utils.getStripeByTime(sat, righttilt, time.start, time.end, Cesium.JulianDate.secondsDifference(time.end, time.start));
                var wkt = this.convertToWKT(positions);
                var geom = wktreader.read(wkt);
                if(geom.contains(geometry)){
                    return righttilt;
                }
            }

            return this._coverSmallArea(sat, geometry, time, tilt, offset+TILTSTEP);
        }
    },

    /**
     * 判断是否相交
     * @param geometry1
     * @param plot
     * @returns {boolean}
     */
    intersects: function(geometry1, plot) {
        var wkt1 = this.convertToWKT2(geometry1);

        var geom1 = wktreader.read(wkt1);
        var geoms = this.splitPolygon(geom1);

        var intersects = false;
        geoms.forEach(function (geom) {
            if (geom.intersects(plot)) {
                intersects = true;
            }
        });

        return intersects;
    },
    /**
     * 将坐标转化为wkt
     * @param positions
     * @returns {Array}
     */
    convertToWKT: function(positions) {
        if (positions[0] != positions[positions.length - 1]) {
            positions.push(positions[0]);
        }
        var wkt = [];
        positions.forEach(function (position) {
            var pos = ellipsoid.cartesianToCartographic(position);
            wkt.push(pos.longitude * 180 / Math.PI + " " + pos.latitude * 180 / Math.PI);
        });
        wkt = wkt.join(",");
        wkt = "POLYGON((" + wkt + "))";
        return wkt;
    },

    convertToWKT2: function(positions){
        if (positions[0] != positions[positions.length - 1]) {
            positions.push(positions[0]);
        }
        var wkt = [];
        positions.forEach(function (position) {
            wkt.push(position.longitude * 180 / Math.PI + " " + position.latitude * 180 / Math.PI);
        });
        wkt = wkt.join(",");
        wkt = "POLYGON((" + wkt + "))";
        return wkt;
    },

    /**
     * 根据180度经线分割polygon
     * @param polygon
     * @returns {*[]}
     */
    splitPolygon: function(polygon) {
        var extent = polygon.computeEnvelopeInternal();
        var w = Math.abs(extent.maxx - extent.minx);
        if(w>180){
            var points = polygon.shell.points;
            var splitindexs = [];
            for (var i = 0; i < points.length - 1; i++) {
                if (points[i].x * points[i + 1].x < 0) {
                    if (Math.abs(points[i].x - points[i + 1].x) > 180) {
                        splitindexs.push(i);
                    }
                }
            }

            if (splitindexs.length) {
                var jsts = require("jsts");
                var factory = new jsts.geom.GeometryFactory();
                var index1 = splitindexs[0];
                var index2 = splitindexs[1];
                var polypos = points.splice(index1 + 1, index2 - index1);
                if (!polypos[0].equals(polypos[polypos.length - 1])) {
                    polypos.push(polypos[0]);
                }

                var linering1 = factory.createLinearRing(polypos);
                var poly1 = factory.createPolygon(linering1, null);
                var linering2 = factory.createLinearRing(points);
                var poly2 = factory.createPolygon(linering2, null);

                return [poly1, poly2];
            } else {
                return [polygon];
            }
        }else{
            return [polygon];
        }
    },

    /**
     * 精度优化
     * @param sat
     * @param start
     * @param wkt
     * @param isstart
     * @returns {*}
     */
    optimize: function(sat, start, plot, isstart) {
        var minstep = this.min_step_seconds / Cesium.TimeConstants.SECONDS_PER_MINUTE;
        var step = this.step_seconds / Cesium.TimeConstants.SECONDS_PER_MINUTE;
        var minutes, julianDate;

        var time = start;
        for (minutes = 0; minutes <= step;) {
            var delta = isstart ? 1 : -1;
            julianDate = Cesium.JulianDate.addMinutes(start, delta * minutes, new Cesium.JulianDate());

            var pos = Utils.getPositionByTime(sat, julianDate);
            var nextpos = Utils.getPositionByTime(sat, Cesium.JulianDate.addMinutes(julianDate, delta * minstep, new Cesium.JulianDate()));
            var lrpos = Utils.getMaxTiltPositions(sat, pos, sat.max_tilt);
            var halfpos = Utils.getMaxTiltPositions(sat, pos, sat.max_tilt/2);
            var lp1 = ellipsoid.cartesianToCartographic(lrpos[0]);
            var hlp1 = ellipsoid.cartesianToCartographic(halfpos[0]);
            var cp1 = ellipsoid.cartesianToCartographic(pos);
            var rp1 = ellipsoid.cartesianToCartographic(lrpos[1]);
            var hrp1 = ellipsoid.cartesianToCartographic(halfpos[1]);

            lrpos = Utils.getMaxTiltPositions(sat, nextpos, sat.max_tilt);
            var halfrpos = Utils.getMaxTiltPositions(sat, nextpos, sat.max_tilt/2);
            var lp2 = ellipsoid.cartesianToCartographic(lrpos[0]);
            var hlp2 = ellipsoid.cartesianToCartographic(halfrpos[0]);
            var cp2 = ellipsoid.cartesianToCartographic(nextpos);
            var rp2 = ellipsoid.cartesianToCartographic(lrpos[1]);
            var hrp2 = ellipsoid.cartesianToCartographic(halfrpos[1]);

            var poly = [lp1, lp2, hlp2, cp2, hrp2, rp2, rp1, hrp1, cp1, hlp1];

            if (this.intersects(poly, plot)) {
                time = julianDate.clone();
                break;
            }

            minutes = minutes + minstep;
        }

        return time;
    }
};



process.on("message", function(data){
    new Worker(data);
});